home *** CD-ROM | disk | FTP | other *** search
/ MacHack 1996 / MacHack 1996.toast / Hacks / Hacks ’92 / IR Man™ / 2BufRecordToBufCmd.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-06-06  |  15.9 KB  |  546 lines  |  [TEXT/MPS ]

  1. /*______________________________________________________*/
  2. /*                      Sound I/O Demo                    */
  3. /*                          by                          */
  4. /*                  RICHARD P. COLLYER                  */
  5. /*              Developer Technical Support             */
  6. /*                 Apple Computer, Inc.                 */
  7. /*                       11/14/91                       */
  8. /*______________________________________________________*/
  9.  
  10. /*
  11.  
  12.  
  13. */
  14.  
  15. /**********************************/
  16. /* #includes                      */
  17. /**********************************/
  18.  
  19. #include    "2BufRecordToBufCmd.h"
  20. #include     "atob.h"
  21. #include    "PlayMovie.h"
  22.  
  23. /**********************************/
  24. /* Application Globals              */
  25. /**********************************/
  26.  
  27. SPBPtr                gRecordStruct;
  28. Handle                gBufferHandle[kNumberOfBuffers];
  29. Fixed                gSampleRate;
  30. long                gInternalBuffer;
  31. long                gSoundRefNum = 0;
  32. unsigned long        gSampleAreaSize;        // size of the sample area in the snd handle
  33. int                    gHeaderSize;            // Size of the Header to be skipped in for the bufferCmd
  34. short                gDataStart;                // Size of the Entire 'snd ' header
  35. short                gSampleSize;
  36. short                gNumberOfChannels;
  37. short                gWhichRecordBuffer = 0;
  38. OSErr                gError;
  39. OSType                gCompression;
  40.  
  41. short                gWaitingToSend = 0;
  42. short                gSendingData = 0;
  43. short                gNetBufferSize = 0;
  44. short                gStandardBuffSize;
  45. Ptr                    gNetBuffers[kNumberOfBuffers];
  46. Ptr                    gCurrentNetBufferPoint;
  47. short                gCurrentNetBuffer;
  48.  
  49.  
  50. /**********************************/
  51. /* TestTheSystem                  */
  52. /**********************************/
  53.  
  54. void TestTheSystem (void)
  55.  
  56. /* use Gestalt to make sure the app will work on the system*/
  57.  
  58. {
  59.     long            feature;
  60.     
  61.     gError = Gestalt(gestaltSoundAttr, &feature);
  62.     if (!gError) {
  63.         /* First Check to see that Sound Input is available */
  64.         if ( !(feature & (1 << gestaltHasSoundInputDevice)) )
  65.             ExitWithMessage ("No Sound Input Device, so The app can't run", 0);
  66.             
  67.         /* Second Check to see that the hardware supports stereo, if not then I can't
  68.         record and play sounds at the same time */
  69.         if ( !(feature & (1 << gestaltStereoCapability)) )
  70.             ExitWithMessage ("No Stereo support, so The app can't run", 0);
  71.         }
  72.     else
  73.         ExitWithMessage ("Gestalt failed in TestTheSystem", gError);
  74.         
  75.     return;
  76. }
  77.  
  78. /**********************************/
  79. /* GetSoundDeviceInfo              */
  80. /**********************************/
  81.  
  82. void GetSoundDeviceInfo (void)
  83.  
  84. /* Extract the information about the Sound Input Device to build the Sound Header */
  85.  
  86. {
  87.     long        value;
  88.     
  89.     /* Get the sample rate information for the snd header */
  90.     
  91.     gSampleRate = 0x56EE8BA3;  /*  11 KHz = 0x2B7745D1 22 KHz = 0x56EE8BA3*/
  92.     
  93.     gError = SPBSetDeviceInfo (gSoundRefNum,siSampleRate, (Ptr) &gSampleRate);
  94.     if (gError != noErr)
  95.         ExitWithMessage ("SPBGetDeviceInfo failed in GetSoundDeviceInfo while looking at siSampleRate", gError);
  96.  
  97.     gError = SPBGetDeviceInfo (gSoundRefNum,siSampleRate, (Ptr) &gSampleRate);
  98.     if (gError != noErr)
  99.         ExitWithMessage ("SPBGetDeviceInfo failed in GetSoundDeviceInfo while looking at siSampleRate", gError);
  100.  
  101.     /* Get the sample size information for the snd header */
  102.         
  103.     gError = SPBGetDeviceInfo (gSoundRefNum,siSampleSize, (Ptr) &gSampleSize);
  104.     if (gError != noErr)
  105.         ExitWithMessage ("SPBGetDeviceInfo failed in GetSoundDeviceInfo while looking at siSampleSize", gError);
  106.         
  107.     /* Get the compression type information for the snd header */
  108.         
  109.     gError = SPBGetDeviceInfo (gSoundRefNum,siCompressionType, (Ptr) &gCompression);
  110.     if (gError != noErr)
  111.         ExitWithMessage ("SPBGetDeviceInfo failed in GetSoundDeviceInfo while looking at siCompressionType", gError);
  112.         
  113.     /* Get the number of input channels for the snd header */
  114.         
  115.     gError = SPBGetDeviceInfo (gSoundRefNum,siNumberChannels, (Ptr) &gNumberOfChannels);
  116.     if (gError != noErr)
  117.         ExitWithMessage ("SPBGetDeviceInfo failed in GetSoundDeviceInfo while looking at siNumberChannels", gError);
  118.         
  119.     /* Get the size of the internal sound buffer for the snd buffer */
  120.         
  121.     gError = SPBGetDeviceInfo (gSoundRefNum,siDeviceBufferInfo, (Ptr) &gInternalBuffer);
  122.     if (gError != noErr)
  123.         ExitWithMessage ("SPBGetDeviceInfo failed in GetSoundDeviceInfo while looking at siDeviceBufferInfo", gError);
  124.         
  125.     value = kMilliSecondsOfSound;
  126.     gError = SPBMillisecondsToBytes(gSoundRefNum, &value);
  127.     if (gError != noErr)
  128.         ExitWithMessage ((char *)"\pSPBMillisecondsToBytes failed in GetSoundDeviceInfo", gError);
  129.  
  130.     /* Round the buffer size to a multiple of the internal buffer size */
  131.     gSampleAreaSize = (value / gInternalBuffer) * gInternalBuffer;
  132.  
  133.     return;
  134. }
  135.  
  136. /**********************************/
  137. /* SetUpSounds                      */
  138. /**********************************/
  139.  
  140. void SetUpSounds (Handle *bufferHandle, short *headerSize)
  141.  
  142. /* SetUpSounds is a routine which allocates a snd buffer with the proper header
  143. and sample size, passing the handle and the real header size back to the caller. */
  144.  
  145. {
  146.     GetSoundDeviceInfo();
  147.     
  148.     /* Allocate largest Handle we could need */
  149.     *bufferHandle = NewHandle(gSampleAreaSize + kEstimatedHeaderSize);
  150.     gError = MemError();
  151.     if (gError != noErr || *bufferHandle == nil)
  152.         ExitWithMessage ("NewHandle failed in SetUpSounds", gError);
  153.     
  154.     /* Set up the header. After this call, we'll know how big the header size is */
  155.     gError = SetupSndHeader (*bufferHandle, gNumberOfChannels, gSampleRate, gSampleSize, gCompression, 
  156.                             kMiddleC, 0, headerSize);
  157.     if (gError != noErr)
  158.         ExitWithMessage ("SetupSndHeader failed in SetUpSounds", gError);
  159.     
  160.     /* Size the handle down to the size we really need */    
  161.     SetHandleSize(*bufferHandle, (Size) *headerSize + gSampleAreaSize);
  162.     gError = MemError();
  163.     if (gError != noErr)
  164.         ExitWithMessage ("SetHandleSize failed in SetUpSounds", gError);
  165.     
  166.     /* Move the handle high and lock it  */
  167.     MoveHHi (*bufferHandle);
  168.     gError = MemError();
  169.     if (gError != noErr)
  170.         ExitWithMessage ("MoveHHi failed in SetUpSounds", gError);
  171.  
  172.     HLock (*bufferHandle);
  173.     gError = MemError();
  174.     if (gError != noErr)
  175.         ExitWithMessage ("HLock failed in SetUpSounds", gError);
  176.  
  177.     return;
  178. }
  179.  
  180. /**********************************/
  181. /* FindHeaderSize                  */
  182. /**********************************/
  183.  
  184. int    FindHeaderSize (void)
  185.  
  186. /* This routine returns the number of bytes of the buffer we need to skip
  187. when calling bufferCmd.  The first several bytes of the sound header need to be skipped 
  188. so that the bufferCmd will be pointing at a SoundHeader Record and not an 'snd ' resource 
  189. header. The equations which are used in this routine are from Inside Macintosh VI page 20-22 */
  190.  
  191. {
  192.     int        headerSize;
  193.     short    highByte, lowByte;
  194.     short    format, numberOfSynths, numberOfCommands;
  195.     
  196.     highByte = **gBufferHandle[0];
  197.     lowByte = *(*gBufferHandle[0] + 1);
  198.     format = (highByte << 8) + lowByte;
  199.     
  200.     switch (format) {
  201.         case 1:        /* Format 1 snd */
  202.             headerSize = kBaseHeaderSize;
  203.             
  204.             // find the number of Synths in the snd header
  205.             highByte = *(*gBufferHandle[0] + 2);
  206.             lowByte = *(*gBufferHandle[0] + 3);
  207.             numberOfSynths = (highByte << 8) + lowByte;
  208.             headerSize += numberOfSynths * kSynthSize;
  209.             
  210.             // find the number of commands in the 'snd ' header
  211.             highByte = *(*gBufferHandle[0] + headerSize - 2);
  212.             lowByte = *(*gBufferHandle[0] + headerSize - 1);
  213.             numberOfCommands = (highByte << 8) + lowByte;
  214.             headerSize += numberOfCommands * kCmdSize;
  215.             break;
  216.  
  217.         case 2:        /* Format 2 snd */
  218.             headerSize = kBaseHeaderSize;
  219.             
  220.             // find the number of commands in the 'snd ' header
  221.             highByte = *(*gBufferHandle[0] + 4);
  222.             lowByte = *(*gBufferHandle[0] + 5);
  223.             numberOfCommands = (highByte << 8) + lowByte;
  224.             headerSize += numberOfCommands * kCmdSize;
  225.             break;
  226.  
  227.         default:
  228.             break;
  229.         }
  230.  
  231.     return (headerSize);
  232. }
  233.  
  234. /**********************************/
  235. /* BuildRecordStruct              */
  236. /**********************************/
  237.  
  238. void BuildRecordStruct (Handle bufferHandle)
  239.  
  240. /* build the gRecordStruct pointer and fill in the fields */
  241.  
  242. {
  243.     gRecordStruct = (SPBPtr) NewPtr(sizeof (SPB));
  244.     if (gRecordStruct == nil)
  245.         ExitWithMessage ("NewPtr failed in BuildRecordStruct", gError);
  246.  
  247.     gRecordStruct->inRefNum = gSoundRefNum;
  248.     gRecordStruct->count =  gSampleAreaSize;
  249.     gRecordStruct->milliseconds = 0;
  250.     gRecordStruct->bufferLength = gSampleAreaSize;
  251.     gRecordStruct->bufferPtr = (Ptr) ((*bufferHandle) + gDataStart);
  252.     gRecordStruct->completionRoutine = (ProcPtr) MyRecComp;
  253.     gRecordStruct->interruptRoutine = nil;
  254.     gRecordStruct->userLong = SetCurrentA5();
  255.     gRecordStruct->error = 0;
  256.     gRecordStruct->unused1 = 0;
  257.     
  258.     return;
  259. }
  260.  
  261.  
  262. /**********************************/
  263. /* ExitWithMessage                  */
  264. /**********************************/
  265.  
  266. void ExitWithMessage (char *message, OSErr error)
  267.  
  268. /* All errors are passed to this routine to display a message and the error result.
  269. it also exits the application, because this sample demos the sound manager, not 
  270. error handleing.  */
  271.  
  272. {
  273.     GrafPtr                    savePort;
  274.     DialogPtr                myDialog;
  275.     short                    itemtype, itemHit;
  276.     Handle                    itemHand;
  277.     Rect                    itemRect;
  278.     char                    *errStrPtr, errStr[256];
  279.  
  280.     errStrPtr = (char *)&errStr;
  281.  
  282.     GetPort(&savePort);
  283.     myDialog = GetNewDialog(kErrorDialogID, nil, (WindowPtr) -1);
  284.     SetPort(myDialog);
  285.     
  286.     NumToString(error,errStrPtr);
  287.     GetDItem(myDialog,kErrNumStatText,&itemtype,&itemHand,&itemRect);
  288.     SetIText(itemHand, errStrPtr);
  289.  
  290.     GetDItem(myDialog,kMsgStatText,&itemtype,&itemHand,&itemRect);
  291.     SetIText(itemHand, message);
  292.  
  293.     do {
  294.         ModalDialog(nil,&itemHit);
  295.         } while (itemHit != kOKButton);
  296.             
  297.     DisposDialog(myDialog);
  298.     SetPort(savePort);
  299.     
  300.     TimeToQuit ();
  301. }
  302.  
  303.  
  304. /**********************************/
  305. /* TimeToQuit                      */
  306. /**********************************/
  307.  
  308. void TimeToQuit (void)
  309.  
  310. /* Once I am out of the loop it is time to clean up - stop the currently playing sound,
  311. Dispose of the Channel, close the input driver, and dispose of the Buffer handles and
  312. gRecordStruct Ptr. */
  313.  
  314. {
  315.     short                index, recordingStat, meterlevel;
  316.     unsigned long        totalSamples, numberOfSamples, totalMSec, numberOfMSec;
  317.     
  318.     // check each global to make sure they were allocated before disposing of them
  319.     
  320.     if (gSoundRefNum != 0) {
  321.         gError = SPBGetRecordingStatus (gSoundRefNum, &recordingStat, &meterlevel,
  322.                                 &totalSamples, &numberOfSamples, &totalMSec, &numberOfMSec);
  323.         if (gError != noErr)
  324.             DebugStr("\SPBGetRecordingStatus failed in TimeToQuit (type 'g' return)");
  325.             
  326.         if (recordingStat > 0) {
  327.             // make sure that recording has stopped before I close the sound driver
  328.             gError = SPBStopRecording (gSoundRefNum);
  329.             if (gError != noErr)
  330.                 DebugStr("\pSPBStopRecording failed in TimeToQuit (type 'g' return)");
  331.             }
  332.             
  333.         gError = SPBCloseDevice (gSoundRefNum);
  334.         if (gError != noErr)
  335.             DebugStr("\pSPBCloseDevice failed in TimeToQuit (type 'g' return)");
  336.         }
  337.         
  338.     if (gBufferHandle[0] != nil)
  339.         for (index = 0; index < kNumberOfBuffers; ++index)
  340.             DisposeHandle (gBufferHandle[index]);
  341.  
  342.     if (gRecordStruct != nil)
  343.         DisposePtr ((Ptr) gRecordStruct);
  344.     
  345. }
  346.  
  347. /**********************************/
  348. /* main                              */
  349. /**********************************/
  350.  
  351. void
  352. InitSoundStuff()
  353. {
  354.     short                index, contOnOff = 1;
  355.     short                dataSize;
  356.     
  357.  
  358.     /* Open sound input drive (whichever one is selected in the sound cdev) */
  359.     
  360.     gError = SPBOpenDevice (kDefaultDriver, siWritePermission, &gSoundRefNum);
  361.     if (gError != noErr)
  362.         ExitWithMessage ("SPBOpenDevice failed in main", gError);
  363.         
  364.     /* turn on continuous recording */
  365.         
  366.     gError = SPBSetDeviceInfo (gSoundRefNum,siContinuous, (Ptr) &contOnOff);
  367.     if (gError != noErr)
  368.         ExitWithMessage ("SPBSetDeviceInfo failed in main", gError);
  369.         
  370.     /* build the kNumberOfBuffers snd Buffers */
  371.         
  372.     for (index = 0; index < kNumberOfBuffers; ++index)
  373.         SetUpSounds (&gBufferHandle[index], &gDataStart);
  374.     
  375.     /* determine the part of the header which needs to be skipped before calling bufferCmd */
  376.     
  377.     gHeaderSize = FindHeaderSize();
  378.     
  379.     if (gHeaderSize != 20)
  380.         Debugger();
  381.         
  382.     /* build the gRecordStruct pointer and fill in the fields */
  383.         
  384.     BuildRecordStruct (gBufferHandle[gWhichRecordBuffer]);
  385.  
  386.     /* Build Network Data Buffers */
  387.     gStandardBuffSize = GetHandleSize(gBufferHandle[gWhichRecordBuffer]);
  388.     
  389.     for (index = 0; index < kNumberOfBuffers; ++index)
  390.         gNetBuffers[index] = NewPtr(kNetBufferSize);
  391.         
  392.     gCurrentNetBufferPoint = gNetBuffers[0];
  393.     gCurrentNetBuffer = 0;
  394. }
  395.  
  396. void
  397. StartRecord() {
  398.     gError = SPBRecord (gRecordStruct, true); // start recording
  399.     if (gError != noErr)
  400.         ExitWithMessage ("SPBRecord failed in main", gError);
  401. }
  402.  
  403. long
  404. SendSoundData() {
  405.     short        dataSize;
  406.     short        extraData;
  407.     long        returnedData = 0;
  408.     Ptr                sndDataPtr;
  409.     long            sndDataLen;
  410.     EventRecord     myEvent;
  411.     
  412.     gWaitingToSend = 1;
  413.     
  414.     while ((gWaitingToSend == 1) && (!Button())) {
  415.         WaitNextEvent(0, &myEvent, 0, nil);
  416.         playMovies(&myEvent);
  417.  
  418.     }
  419.     
  420.     
  421.     if (!Button()) {
  422.         gSendingData = 1;
  423.                 
  424.         dataSize = gNetBufferSize;
  425.         extraData = gCurrentNetBuffer;
  426.         extraData = NextBuffer (extraData);
  427.         sndDataLen = *((long *) gNetBuffers[extraData]);
  428.         sndDataPtr = gNetBuffers[extraData] + sizeof(long);
  429.         returnedData = AnalizeData((unsigned char *)(sndDataPtr), sndDataLen);
  430.             
  431.         gSendingData = 0;
  432.     }
  433.     else
  434.     {
  435.         extraData = 1;
  436.         dataSize = sizeof(short);
  437. /*        SendData(&dataSize, (Ptr)&extraData); */
  438.     }
  439.     return(returnedData);
  440. }
  441.  
  442. void
  443. SoundCleanUpAndQuit() {
  444.     short                dataSize;
  445.  
  446.     gNetBufferSize = 0;
  447.     dataSize = sizeof(short);
  448. /*    SendData(&dataSize, (Ptr)&gNetBufferSize); */
  449.  
  450.     // quitting time
  451.         
  452.     if (gRecordStruct->error < noErr)
  453.         ExitWithMessage ((char *)"\pAn error occurred while recording", gRecordStruct->error);
  454.     else
  455.         TimeToQuit ();
  456. }
  457.  
  458. /**********************************/
  459. /* MyRecComp                      */
  460. /**********************************/
  461.  
  462. pascal void MyRecComp (SPBPtr inParamPtr)
  463.  
  464. // This is the Completion Routine which is called every time the recording Buffer,
  465. // is full.  This routine needs to setup A5 to be the application's A5 which was being
  466. // saved in the userLong of the parameter block.
  467.  
  468. // Due to the MPW C compiler optimization scheme, access to global arrays will be pointed
  469. // to in an address register as an offset of A5.  This will happen before we have a chance
  470. // to set A5 to our application's A5.  To avoid this we need to first restore our A5 and
  471. // then call the completion routine.
  472.  
  473. {
  474.     long                storeA5;
  475.     
  476.     /* Set A5 so the completion routine has access to the Application Globals */
  477.  
  478.  
  479.      storeA5 = SetA5 (inParamPtr->userLong);
  480.  
  481.     RealCompletion ();
  482.  
  483.     storeA5 = SetA5 (storeA5);
  484.  
  485.     return;
  486. }
  487.  
  488. /**********************************/
  489. /* RealCompletion                  */
  490. /**********************************/
  491.  
  492. void RealCompletion (void)
  493.  
  494. // Setup the current snd handle to have the correct size of the sample data we've just
  495. // finished recording.  The size of the sample data is in the count field of the
  496. // recording parameter block.  Then play this new buffer of data.  Finally, we will
  497. // start another recording after switching to the other buffer.
  498.  
  499. {
  500.     OSErr            err;
  501.     SoundHeaderPtr    header;
  502.  
  503.     // if there has been an error then do nothing
  504.     if (gRecordStruct->error < 0) return;
  505.     
  506.     // get pointer to SoundHeader and update length value in the header
  507.     header = (SoundHeaderPtr) (*(gBufferHandle[gWhichRecordBuffer]) + gHeaderSize);
  508.     header->length = gRecordStruct->count;
  509.     
  510. /*    gNetBuffer = *gBufferHandle[gWhichRecordBuffer]; */
  511.     
  512.     /* If there is enough room, blockmove more in  */
  513.     if (((gCurrentNetBufferPoint - gNetBuffers[gCurrentNetBuffer]) + gStandardBuffSize + 1024) <
  514.             kNetBufferSize) { 
  515.         BlockMove ((Ptr)&header->length,
  516.                         gCurrentNetBufferPoint, sizeof(long));
  517.         gCurrentNetBufferPoint += sizeof(long);
  518.  
  519.         BlockMove (&(header->sampleArea[0]), gCurrentNetBufferPoint, header->length);
  520.         gCurrentNetBufferPoint += gStandardBuffSize;
  521.         
  522.     }
  523.     
  524.     if (gWaitingToSend == 1) {
  525.         gNetBufferSize = (gCurrentNetBufferPoint - gNetBuffers[gCurrentNetBuffer]);
  526.  
  527.         gCurrentNetBuffer = NextBuffer (gCurrentNetBuffer); // move on to next buffer
  528.         gCurrentNetBufferPoint = gNetBuffers[gCurrentNetBuffer];
  529.         gWaitingToSend = 0;
  530.     }
  531.     
  532. /*    PlayBuffer (gBufferHandle[gWhichRecordBuffer]);    */    // Play the buffer
  533.     
  534.     gWhichRecordBuffer = NextBuffer (gWhichRecordBuffer);    // move on to next buffer
  535.     
  536.      // update gRecordStruct
  537.      gRecordStruct->bufferPtr = (*(gBufferHandle[gWhichRecordBuffer]) + gDataStart);
  538.     gRecordStruct->milliseconds = 0;
  539.     gRecordStruct->count = gSampleAreaSize;
  540.     gRecordStruct->bufferLength = gSampleAreaSize;
  541.     
  542.     err = SPBRecord (gRecordStruct, true);        // queue up another record
  543.     if (err) 
  544.         DebugStr ("\pSPBRecord died in RealCompletion");
  545. }
  546.